/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.common.query;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import java.io.Serial;
import java.io.Serializable;

/**
 * 查询条件封装。 其中IS_NULL, IS_NOT_NULL，可能不会用到，系统默认约束参与搜索的列不能为空。
 * <p>
 * 对于复杂的查询，请另外实现自己的方法
 *
 * @author 挺好的 2023年05月30日 15:54
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Filter implements Serializable {

    @Serial
    private static final long serialVersionUID = 8679066702031857877L;

    /**
     * 查询的属性
     */
    private String property;

    /**
     * 操作符
     */
    private Operator operator;

    /**
     * 值
     */
    private Object value;

    /**
     * 是否忽略大小写
     */
    private boolean ignoreCase;

    /**
     * 是否校验 value == null
     */
    private boolean verifyNullValue;

    /**
     * 是否有效。 有效的条件：
     * 1. 操作符不能为空
     * 2. 属性名称不能为空
     * 3. isVerifyNullValue == true，value 则不可为空
     *
     * @return 有效，返回true，否则返回false
     *
     * @see org.apache.commons.lang3.ObjectUtils#isNotEmpty(Object)
     */
    public boolean isValid () {

        // 校验值是否为空
        if (this.verifyNullValue) {
            return this.operator != null && StringUtils.isNotEmpty(this.property) && ObjectUtils.isNotEmpty(this.value);
        }

        return this.operator != null && StringUtils.isNotEmpty(this.property);
    }

    /**
     * 获取等于查询条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 等于条件
     */
    public static Filter eq (String property, Object value) {
        return eq(property, value, false);
    }

    /**
     * 创建等于查询条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     * @param ignoreCase
     *         是否忽略大小写
     *
     * @return 等于条件
     */
    public static Filter eq (String property, Object value, boolean ignoreCase) {
        return new Filter(property, Operator.EQ, value, ignoreCase, true);
    }

    /**
     * 创建not equals 查询条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return not equals条件
     */
    public static Filter ne (String property, Object value) {
        return new Filter(property, Operator.NE, value, false, true);
    }

    /**
     * 创建not equals 查询条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     * @param ignoreCase
     *         是否忽略大小写
     *
     * @return not equals条件
     */
    public static Filter ne (String property, Object value, boolean ignoreCase) {
        return new Filter(property, Operator.NE, value, ignoreCase, true);
    }


    /**
     * 构造大于条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 大于条件
     */
    public static Filter gt (String property, Object value) {
        return new Filter(property, Operator.GT, value, false, true);
    }

    /**
     * 构造小于条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 小于条件
     */
    public static Filter lt (String property, Object value) {
        return new Filter(property, Operator.LT, value, false, true);
    }

    /**
     * 构造大于等于条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 大于等于条件
     */
    public static Filter ge (String property, Object value) {
        return new Filter(property, Operator.GE, value, false, true);
    }

    /**
     * 构造小于等于条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 小于等于条件
     */
    public static Filter le (String property, Object value) {
        return new Filter(property, Operator.LE, value, false, true);
    }

    /**
     * 构造模糊查询条件。左右匹配（%keyword%）
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 模糊查询条件
     */
    public static Filter like (String property, Object value) {
        return new Filter(property, Operator.LIKE, value, false, true);
    }

    /**
     * 构造模糊查询条件。右匹配（keyword%）
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 模糊查询条件
     */
    public static Filter likeRight (String property, Object value) {
        return new Filter(property, Operator.RIGHT_LIKE, value, false, true);
    }

    /**
     * 构造在子集中的条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 子集条件
     */
    public static Filter in (String property, Object value) {
        return new Filter(property, Operator.IN, value, false, true);
    }

    /**
     * 构造是否在某个范围内的条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值。必须以Pair形式传入。
     *
     * @return 某个范围的条件
     *
     * @see org.apache.commons.lang3.tuple.Pair
     */
    public static Filter between (String property, Object value) {
        return new Filter(property, Operator.BETWEEN, value, false, true);
    }

    /**
     * or 条件
     *
     * @param property
     *         属性名称
     * @param value
     *         查询条件
     *
     * @return
     */
    public static Filter or (String property, Object value) {

        if (value == null) {
            return null;
        }

        return new Filter(property, Operator.OR, value, false, true);
    }

    /**
     * 是否为空
     *
     * @param property
     *         属性名称
     *
     * @return 为空条件
     */
    public static Filter isNull (String property) {
        return new Filter(property, Operator.IS_NULL, null, false, false);
    }

    /**
     * 是否不为空
     *
     * @param property
     *         属性名称
     *
     * @return 不为空条件
     */
    public static Filter isNotNull (String property) {
        return new Filter(property, Operator.IS_NOT_NULL, null, false, false);
    }

    /**
     * 是否为真
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 为真的条件
     */
    public static Filter isTrue (String property, Object value) {
        return new Filter(property, Operator.IS_TRUE, value, false, true);
    }

    /**
     * 是否为真
     *
     * @param property
     *         属性名称
     *
     * @return 为真的条件
     */
    public static Filter isTrue (String property) {
        return new Filter(property, Operator.IS_TRUE, true, false, true);
    }

    /**
     * 是否为假
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return 为假的条件
     */
    public static Filter isFalse (String property, Object value) {
        return new Filter(property, Operator.IS_FALSE, value, false, true);
    }

    /**
     * 是否为假
     *
     * @param property
     *         属性名称
     *
     * @return 为假的条件
     */
    public static Filter isFalse (String property) {
        return new Filter(property, Operator.IS_FALSE, false, false, true);
    }

    /**
     * 是true 还是 false
     *
     * @param property
     * @param value
     *         值
     *
     * @return {@link vip.dexian.common.query.Filter}
     */
    public static Filter isTrueOrFalse (String property, Object value) {
        if (value == null) {
            return null;
        }

        Boolean boolValue = (Boolean) value;

        return new Filter(property, Boolean.TRUE.equals(boolValue) ? Operator.IS_TRUE : Operator.IS_FALSE, value, false,
                true
        );
    }


    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }

        if (o == null || this.getClass() != o.getClass()) {
            return false;
        }

        Filter filter = (Filter) o;

        return new EqualsBuilder().append(this.property, filter.property).isEquals();
    }

    @Override
    public int hashCode () {
        return new HashCodeBuilder(17, 37).append(this.property).toHashCode();
    }
}
